package cn.aop.anno.tx;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * {@code @Aspect}: 定义该组件类是一个切面类。
 * <p>
 * 切入点语法详解:
 *
 * @author yexi
 * @link <a href="https://blog.csdn.net/zhengchao1991/article/details/53391244"/>
 * @link <a href="https://blog.csdn.net/qq_33704186/article/details/88413438"/>
 */
@Component
@Aspect
public class CustomTransactionManager {

    /**
     * 定义切入点 Where
     */
    @Pointcut("execution(* cn.aop.anno.service.impl.*ServiceImpl.*(..))")
    public void txPointcut() {
        System.out.println("切入点只起到配置定义作用，此方法不会进行实际的调用");
    }

    @Pointcut("txPointcut()")
    public void allPointcut() {
        System.out.println("可以定义多个切入点，且切入点与切入点可以进一步嵌套");
    }

    @Pointcut("!txPointcut() && !allPointcut()")
    public void dasPointcut() {
        System.out.println("可以定义多个切入点，且切入点与切入点可以进一步嵌套");
    }

    /**
     * {@code @Before}: 前置通知。作用于目标方法执行之前。
     */
    @Before("txPointcut()")
    public void begin() {
        System.out.println("开启事务-注解版本");
    }

    /**
     * {@code @AfterReturning}: 返回通知。作用于目标方法正常返回之后。
     */
    @AfterReturning("txPointcut()")
    public void commit() {
        System.out.println("提交事务-注解版本");
    }

    /**
     * {@code @AfterThrowing}: 异常通知。作用于目标方法抛出异常之后。
     */
    @AfterThrowing("txPointcut()")
    public void rollback() {
        System.out.println("回滚事务-注解版本");
    }

    /**
     * {@code @After}: 后置通知。作用于目标方法结束之后(抛异常结束也会调用)。
     */
    @After("txPointcut()")
    public void end() {
        System.out.println("业务处理结束-注解版本");
    }

    /**
     * {@code @Around}: 环绕通知。作用于目标方法执行之前和之后。
     * 环绕通知可以控制目标方法的执行，并决定是否继续执行目标方法或者修改返回值。
     * 由于环绕通知可以控制目标方法的执行，所以相当于完全接管了目标方法的执行流程。如果在其内部执行逻辑后直接返回，其他切点上定义的通知将无法被触发。
     */
    @Around("allPointcut()")
    public void around() {
        System.out.println("环绕通知，因为返回值是void，所以后续的通知都不会执行(除了异常通知)，后续请注释此通知");
    }

    @Around("allPointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Signature signature = joinPoint.getSignature();
        System.out.println("签名的详情信息: " + signature.toLongString());
        if (signature instanceof MethodSignature) {
            MethodSignature methodSignature = (MethodSignature) signature;
            Method method = methodSignature.getMethod();
            System.out.println("切入目标类方法: " + method);
        }
        System.out.println("切入目标类: " + joinPoint.getTarget().getClass());
        Object[] args = joinPoint.getArgs();
        System.out.println("切入目标类的参数: " + Arrays.toString(args));

        return joinPoint.proceed();
    }

}
